﻿using System;
using System.Dynamic;
using System.Text;
using Microsoft.Isam.Esent.Collections.Generic;
using NLog;

namespace TfsGiggle.Core
{
    /// <summary>
    /// Provides TfsGiggle with dynamically created statistics counters persisted via the PersistentDictionary.
    /// Instance is dynamic allowing this construct: Statistics.Instance.ThisIsADynamicCounter.Add(1)
    /// .Instance is dynamic and ThisIsADynamicCounter is backed by a Counter, added to the _counters collection and persisted.
    /// A Counter is handed the _counters so it can update to the dictionary.
    /// </summary>
    public sealed class Statistics : DynamicObject, IDisposable
    {
        /// <summary>
        /// Don't change to private; dynamic will then think it should look for it with TryGetMember
        /// </summary>
        internal readonly RecentList<string> SessionMostRecentSearches = new RecentList<string>(50);
        readonly static Lazy<Statistics> _instance = new Lazy<Statistics>(() => new Statistics());
        PersistentDictionary<string, long> _counters;
        static LoggerWithEventId _logger = (LoggerWithEventId)LogManager.GetCurrentClassLogger(typeof(LoggerWithEventId));

        private Statistics()
        {
        }

        /// <summary>
        /// Open the store for the statistics
        /// </summary>
        /// <param name="folder"></param>
        public void Open(string folder)
        {
            if (string.IsNullOrWhiteSpace(folder))
            {
                const string msg = "folder is null or empty";
                _logger.Error(msg);
                throw new ArgumentNullException(msg);
            }
            _counters = new PersistentDictionary<string, long>(folder);
        }

        /// <summary>
        /// This instance
        /// </summary>
        public static dynamic Instance
        {
            get { return _instance.Value; }
        }

        /// <summary>
        /// Get the property value
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public Counter GetPropertyValue(string propertyName)
        {
            return new Counter(propertyName, _counters);
        }

        /// <summary>
        /// Get the member
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = GetPropertyValue(binder.Name);
            return result == null ? false : true;
        }

        /// <summary>
        /// Try calling the member
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            result = GetPropertyValue(binder.Name);
            return result == null ? false : true;
        }

        private void Dispose(bool isDisposing)
        {
            if( isDisposing )
            {
                _counters.Dispose();
                _counters = null;
            }
        }

        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Produces the xml with collected counters
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            var sb = new StringBuilder("<Statistics><Counters>");
            foreach (var key in _counters.Keys)
            {
                sb.AppendLine(string.Format("<Counter Name=\"{0}\" Value=\"{1}\" />", key, _counters[key]));
            }
            sb.AppendLine("</Counters><MostRecentSearches>");
            foreach (var search in SessionMostRecentSearches)
            {
                sb.AppendLine("<Search>" + search + "</Search>");
            }
            sb.AppendLine("</MostRecentSearches></Statistics>");
            return sb.ToString();
        }
    }
}